package org.jeecg.modules.tankerRegistration.controller;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.huaweicloud.sdk.ocr.v1.model.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.oss.ObsBootUtil;
import org.jeecg.modules.backups.entity.Backups;
import org.jeecg.modules.backups.service.IBackupsService;
import org.jeecg.modules.gpsLog.entity.GpsLog;
import org.jeecg.modules.gpsLog.service.IGpsLogService;
import org.jeecg.modules.gpsRecord.entity.GpsRecord;
import org.jeecg.modules.gpsRecord.service.IGpsRecordService;
import org.jeecg.modules.identifyRecord.entity.IdentifyRecord;
import org.jeecg.modules.identifyRecord.service.IIdentifyRecordService;
import org.jeecg.modules.online.config.exception.BusinessException;
import org.jeecg.modules.tankerBlack.entity.TankerBlack;
import org.jeecg.modules.tankerBlack.service.ITankerBlackService;
import org.jeecg.modules.tankerRegistration.entity.*;
import org.jeecg.modules.tankerRegistration.scheduled.MyScheduled;
import org.jeecg.modules.tankerRegistration.service.ITankerRegistrationService;
import org.jeecg.modules.tankerRegistration.util.*;
import org.jeecg.modules.tankerRules.entity.TankerRules;
import org.jeecg.modules.tankerRules.service.ITankerRulesService;
import org.jeecgframework.poi.excel.ExcelExportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.ZipOutputStream;

/**
 * @Description: 罐车登记表
 * @Author: jeecg-boot
 * @Date: 2024-10-28
 * @Version: V1.0
 */
@Api(tags = "罐车登记表")
@RestController
@RequestMapping("/tankerRegistration/tankerRegistration")
@Slf4j
public class TankerRegistrationController extends JeecgController<TankerRegistration, ITankerRegistrationService> {
    @Value(value = "${jeecg.uploadType}")
    private String uploadType;

    @Value("${jeecg.path.upload}")
    private String upLoadPath;

    @Autowired
    private ITankerRegistrationService tankerRegistrationService;
    @Autowired
    private ITankerBlackService tankerBlackService;
    @Autowired
    private TemplateMessageUtil templateMessageUtil;
    @Autowired
    private IBackupsService backupsService;
    @Autowired
    private IIdentifyRecordService identifyRecordService;
    @Autowired
    private IGpsRecordService gpsRecordService;
    @Autowired
    private ITankerRulesService tankerRulesService;
    @Autowired
    private IGpsLogService gpsLogService;

    //太维科技公众号
//	 private static final String APPID = "wx35cfb8619f70829d";
//	 private static final String SECRET = "ad9fa5ffe510db2f88d3164c119444c1";
    //中储粮公众号
    private static final String APPID = "wxeba7a1e277a4e2aa";
    private static final String SECRET = "87f4e029ae803656c814d6ef02f0883a";

    /**
     * 分页列表查询
     *
     * @param tankerRegistration
     * @param pageNo
     * @param pageSize
     * @param req
     * @return
     */
    @AutoLog(value = "罐车登记表-分页列表查询")
    @ApiOperation(value = "罐车登记表-分页列表查询", notes = "罐车登记表-分页列表查询")
    @GetMapping(value = "/list")
    public Result<?> queryPageList(TankerRegistration tankerRegistration,
                                   @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                   @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                   HttpServletRequest req) {
        QueryWrapper<TankerRegistration> queryWrapper = QueryGenerator.initQueryWrapper(tankerRegistration, req.getParameterMap());
        Page<TankerRegistration> page = new Page<TankerRegistration>(pageNo, pageSize);
        IPage<TankerRegistration> pageList = tankerRegistrationService.page(page, queryWrapper);
        return Result.OK(pageList);
    }

    @AutoLog(value = "罐车登记表-分页列表查询(APP)")
    @ApiOperation(value = "罐车登记表-分页列表查询(APP)", notes = "罐车登记表-分页列表查询(APP)")
    @GetMapping(value = "/m/list")
    public Result<?> qualityList(TankerRegistration tankerRegistration,
                                 @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                 @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
                                 HttpServletRequest req) {
        QueryWrapper<TankerRegistration> queryWrapper = QueryGenerator.initQueryWrapper(tankerRegistration, req.getParameterMap());
        Page<TankerRegistration> page = new Page<TankerRegistration>(pageNo, pageSize);
        IPage<TankerRegistration> pageList = tankerRegistrationService.page(page, queryWrapper);
        return Result.OK(pageList);
    }

    /**
     * 添加
     *
     * @param tankerRegistration
     * @return
     */
    @AutoLog(value = "罐车登记表-添加")
    @ApiOperation(value = "罐车登记表-添加", notes = "罐车登记表-添加")
    @PostMapping(value = "/add")
    public Result<?> add(@RequestBody TankerRegistration tankerRegistration) {
        tankerRegistrationService.save(tankerRegistration);
        return Result.OK("添加成功！");
    }

    @AutoLog(value = "罐车登记表-司机登记")
    @ApiOperation(value = "罐车登记表-司机登记", notes = "罐车登记表-司机登记")
    @PostMapping(value = "/m/add")
    public Result<?> mAdd(@RequestBody TankerRegistration tankerRegistration) {
        TankerRegistration byPlateNo = tankerRegistrationService.getByPlateNo(tankerRegistration.getPlateNo());
        if (byPlateNo != null) {
            Integer auditStatus = byPlateNo.getAuditStatus();
            if (1 == auditStatus) {
                tankerRegistrationService.removeById(byPlateNo.getId());
            } else if (Arrays.asList(2, 4, 6, 8, 10, 11, 12, 16, 17).contains(auditStatus)) {
                return Result.error("当前车辆正在流程进行中, 请完成全部流程后再登记。");
            }
        }
        TankerBlack black = tankerBlackService.getByPlateOrTrailerNo(tankerRegistration.getPlateNo(),
                tankerRegistration.getTrailerNo());
        if (black != null && black.getEnable() == 1) {
            tankerRegistration.setAuditStatus(14);//黑名单
        }
        TankerRules rules = tankerRulesService.getRules();
        if (rules != null && rules.getCheckEnable()) {
            Integer checkTime = rules.getCheckTime();
            LambdaQueryWrapper<TankerRegistration> wrapper = Wrappers.lambdaQuery();
            wrapper.eq(TankerRegistration::getPlateNo, tankerRegistration.getPlateNo());
            if (checkTime > 0) {//正数：手动设置时间
                // 使用 Calendar 计算 checkTime 天前的日期
                Calendar calendar = Calendar.getInstance();
                calendar.add(Calendar.DAY_OF_MONTH, -checkTime);
                Date date = calendar.getTime();
                wrapper.eq(TankerRegistration::getAuditStatus, 7);//不合格
                wrapper.gt(TankerRegistration::getCreateTime, date);
                // 查询符合条件的记录数
                int count = tankerRegistrationService.count(wrapper);
                if (count > 0) {
                    tankerRegistration.setLastUnqualifiedMark(1);
                }
            } else if (checkTime == 0) {//0：永久
                wrapper.eq(TankerRegistration::getAuditStatus, 7);//不合格
                int count = tankerRegistrationService.count(wrapper);
                if (count > 0) {
                    tankerRegistration.setLastUnqualifiedMark(1);
                }
            } else {//负数：仅限上一次
                wrapper.orderByDesc(TankerRegistration::getCreateTime);
                wrapper.last("limit 1");
                TankerRegistration one = tankerRegistrationService.getOne(wrapper);
                if (one != null && one.getAuditStatus() == 7) {//不合格
                    tankerRegistration.setLastUnqualifiedMark(1);
                }
            }
        }
        tankerRegistrationService.save(tankerRegistration);
        if(StringUtils.isNotBlank(tankerRegistration.getGpsRecordIds())){
            gpsRecordService.updateTankerId(tankerRegistration.getId(), Arrays.asList(tankerRegistration.getGpsRecordIds().split(",")));
        }
        return Result.OK("添加成功！", tankerRegistration.getId());
    }

    // 配置类（调整权重）
    private static final Map<Function<IdentifyRecord, String>, Integer> FIELD_WEIGHTS = new LinkedHashMap<Function<IdentifyRecord, String>, Integer>() {{
        put(IdentifyRecord::getProductType, 20);
        put(IdentifyRecord::getShipmentTime, 20);
        put(IdentifyRecord::getPickupLocation, 20);
        put(IdentifyRecord::getDeliveryLocation, 20);
        put(IdentifyRecord::getUnloadingTime, 20);
    }};

    // 算法接口
    @FunctionalInterface
    interface SimilarityAlgorithm {
        int calculateScore(String source, String target);
    }

    // 新算法实现
    private final SimilarityAlgorithm characterBasedAlgorithm = (source, target) -> {
        if (StringUtils.isBlank(source) || StringUtils.isBlank(target)) {
            return 0;
        }

        // 预先生成字符集合
        Set<Character> targetChars = target.chars()
                .mapToObj(c -> (char) c)
                .collect(Collectors.toSet());

        long matchedCount = source.chars()
                .mapToObj(c -> (char) c)
                .filter(targetChars::contains)
                .count();

        return (int) Math.round((double) matchedCount / source.length() * 100);
    };

    @AutoLog(value = "罐车登记表-AI比对")
    @ApiOperation(value = "罐车登记表-AI比对", notes = "罐车登记表-AI比对")
    @PostMapping(value = "/compare")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> compare(@RequestBody TankerRegistration tankerRegistration) {
        List<IdentifyRecord> records = identifyRecordService.getByTankerId(tankerRegistration.getId());

        if (CollectionUtils.isEmpty(records)) {
            return Result.error("未找到比对记录");
        }

        records.forEach(this::calculateCharacterBasedScore);
        identifyRecordService.updateBatchById(records);

        return Result.OK("比对完成");
    }

    private void calculateCharacterBasedScore(IdentifyRecord record) {
        String textA = Optional.ofNullable(record.getText1()).orElse("");

        // 预先生成字符集合用于快速查找
        Set<Character> textAChars = textA.chars()
                .mapToObj(c -> (char) c)
                .collect(Collectors.toSet());

        int totalScore = FIELD_WEIGHTS.entrySet().stream()
                .mapToInt(entry -> {
                    String fieldValue = Optional.ofNullable(entry.getKey().apply(record)).orElse("");
                    return calculateFieldScore(fieldValue, textAChars) * entry.getValue() / 100;
                })
                .sum();

        record.setText2(String.valueOf(totalScore));
    }

    private int calculateFieldScore(String fieldValue, Set<Character> textAChars) {
        if (StringUtils.isBlank(fieldValue) || textAChars.isEmpty()) {
            return 0;
        }

        long matchedCount = fieldValue.chars()
                .mapToObj(c -> (char) c)
                .filter(textAChars::contains)
                .count();

        double ratio = (double) matchedCount / fieldValue.length();
        return (int) Math.round(ratio * 100);
    }

    @AutoLog(value = "罐车登记表-AI辅助")
    @ApiOperation(value = "罐车登记表-AI辅助", notes = "罐车登记表-AI辅助")
    @PostMapping(value = "/assistant")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> assistant(@RequestBody TankerRegistration tankerRegistration) {
        try {
            // 1. 参数校验
            validateDocumentationFiles(tankerRegistration);

            // 2. 处理三载证明文件
            List<IdentifyRecord> identifyRecords = processDocumentationFile(tankerRegistration.getDocumentation());

            // 3. 处理卸货照片并关联数据
            processUnloadingPhotos(tankerRegistration, identifyRecords);

            identifyRecordService.removeByTankerId(tankerRegistration.getId());

            // 4. 批量保存数据
            identifyRecordService.saveBatch(identifyRecords);

            return Result.OK("操作成功");
        } catch (BusinessException e) {
            return Result.error(e.getMessage());
        } catch (Exception e) {
            log.error("系统处理异常", e);
            return Result.error("系统处理异常，请稍后重试");
        }
    }

    //-------------------- 模块化方法 --------------------
    private void validateDocumentationFiles(TankerRegistration reg) {
        try {
            validateFileCount(reg.getDocumentation(), 1, "三载证明文件");
            validateFileCount(reg.getLastTimeUnloadingPhotos(), 3, "三载卸货磅单照片");
        } catch (BusinessException e) {
            throw new RuntimeException(e);
        }
    }

    private void validateFileCount(String files, int expectedCount, String fieldName) throws BusinessException {
        if (StringUtils.isBlank(files)) {
            throw new BusinessException(fieldName + "不能为空");
        }
        int actualCount = files.split(",").length;
        if (actualCount != expectedCount) {
            throw new BusinessException(fieldName + "数量必须为【" + expectedCount + "】");
        }
    }

    private List<IdentifyRecord> processDocumentationFile(String documentation) throws BusinessException {
        try {
            String fileUrl = documentation.split(",")[0];
            RecognizeGeneralTableResponse response = HuaweiCloudUtil.recognizeGeneralTable(fileUrl);
            return handleDocumentationTableDataOfOcr(response);
        } catch (Exception e) {
            log.error("三载证明文件解析失败: {}", documentation, e);
            throw new BusinessException("三载证明文件解析失败，请检查文件内容");
        }
    }

    private void processUnloadingPhotos(TankerRegistration tankerRegistration, List<IdentifyRecord> records) throws BusinessException {
        String photos = tankerRegistration.getLastTimeUnloadingPhotos();
        String[] photoUrls = photos.split(",");
        // 校验记录数量匹配
        if (records.size() < photoUrls.length) {
            throw new BusinessException("识别记录与照片数量不匹配");
        }
        for (int i = 0; i < photoUrls.length; i++) {
            try {
                String ocrResult = HuaweiCloudUtil.handleGeneralTableDataOfOcr(
                        HuaweiCloudUtil.recognizeGeneralTable(photoUrls[i])
                );
                records.get(i).setText1(ocrResult).setTankerId(tankerRegistration.getId());
            } catch (Exception e) {
                log.error("第{}张卸货照片处理失败: {}", i + 1, photoUrls[i], e);
                throw new BusinessException("第" + (i + 1) + "张卸货照片解析失败");
            }
        }
    }

    @AutoLog(value = "罐车登记表-查询gps停留记录并比对")
    @ApiOperation(value = "罐车登记表-查询gps停留记录并比对", notes = "罐车登记表-查询gps停留记录并比对")
    @PostMapping(value = "/m/GPSSearch")
    @Transactional(rollbackFor = Exception.class)
    public Result<?> GPSSearch(@RequestBody JSONObject request) {
        JSONObject pickUp = request.getJSONObject("pickUp");
        JSONObject unloading = request.getJSONObject("unloading");
        if (pickUp == null || unloading == null) {
            return Result.error("请先查询装卸货地点定位");
        }
        String unloadingLocation = unloading.getString("location");
        String pickUpLocation = pickUp.getString("location");
        if (StringUtils.isBlank(unloadingLocation) || StringUtils.isBlank(pickUpLocation)) {
            return Result.error("未找到对应地点定位,请重新获取");
        }
        String id = request.getString("id");
        String plateNo = request.getString("plateNo");
        Date lastTimePickUpDate1 = request.getDate("lastTimePickUpDate1");//前一载装货时间
        Date lastTimeUnloadingDate1 = request.getDate("lastTimeUnloadingDate1");//前一载卸货时间
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        try {
            List<GpsRecord> pickUpList = handleSearchGps(id, plateNo, pickUpLocation, 1, lastTimePickUpDate1, formatter);
            List<GpsRecord> unLoadList = handleSearchGps(id, plateNo, unloadingLocation, 2, lastTimeUnloadingDate1, formatter);
            List<GpsRecord> list = new ArrayList<>();
            if (pickUpList != null) list.addAll(pickUpList);
            if (unLoadList != null) list.addAll(unLoadList);
            return Result.OK(list);
        } catch (Exception e) {
            log.error("北斗查询失败", e);
            return Result.error("查询失败");
        }
    }

    private List<GpsRecord> handleSearchGps(String id, String plateNo,
                                 String location, Integer transportStatus,
                                 Date date, DateTimeFormatter formatter) throws IOException {
        LocalDateTime localDate = date.toInstant()
                .atZone(ZoneId.systemDefault()).toLocalDateTime();
        long timestamp = localDate.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        LocalDateTime beginDateTime = localDate
                .withHour(0).withMinute(0).withSecond(0);
        LocalDateTime endDateTime = localDate
                .withHour(23).withMinute(59).withSecond(59);
        String s = DateUtils.formatDate(date);
        Map<String, String> params = new HashMap<>();
        params.put("plateNo", plateNo);
        params.put("begintime", beginDateTime.format(formatter));
        params.put("endtime", endDateTime.format(formatter));
        params.put("plateColor", "2");
        // 使用 OkHttp
        String response = HuaweiCloudUtil.sendPost("https://enviroment.tw-tj.com:2443/NewElefence/GetRoutePath", params);
        JSONObject jsonObject = JSONObject.parseObject(response);
        if (jsonObject != null && jsonObject.getInteger("status") == 1001){
            UUID uuid = UUID.randomUUID();
            // 解析并转换送货地和提货地坐标（WGS84转GCJ02）
            CoordinateParser.Doubles lonLat = CoordinateParser.parse(location);
            List<GpsRecord> list = new ArrayList<>();
            JSONObject result = jsonObject.getJSONObject("result");
            JSONArray parkArray = result.getJSONArray("parkArray");
            for (Object park : parkArray) {
                JSONObject parkObject = (JSONObject) park;
                GpsRecord gpsRecord = parkObject.toJavaObject(GpsRecord.class);
                // 解析并转换GPS坐标（原始值转GCJ02）
                CoordinateParser.Doubles gpsPoint = CoordinateParser.parseGpsRecord(
                        gpsRecord.getParkLon(),
                        gpsRecord.getParkLat()
                );
                // 计算距离
                double distance = DistanceCalculator.calculate(
                        lonLat.lon, lonLat.lat,
                        gpsPoint.lon, gpsPoint.lat
                );
                gpsRecord.setDistance(distance);
                gpsRecord.setScore1(distance < 1000.0 ? "1" : "0");
                boolean b = Long.parseLong(gpsRecord.getParkBte()) < timestamp && timestamp < Long.parseLong(gpsRecord.getParkEte());
                gpsRecord.setScore2(b? distance < 1000.0 ? "1" : "0" : "0");
                gpsRecord.setTransportStatus(transportStatus);
                gpsRecord.setTankerId(id);
                gpsRecord.setUuid(uuid.toString());
                list.add(gpsRecord);
            }
            if (!list.isEmpty()) {
                gpsRecordService.saveBatch(list);
            }
            GpsLog gpsLog = new GpsLog();
            gpsLog.setResult("查询成功")
                    .setPlate(plateNo)
                    .setTimeSlot(s);
            gpsLogService.save(gpsLog);
            return list;
        } else {
            return null;
        }
    }

    @AutoLog(value = "罐车登记表-验证(app)")
    @ApiOperation(value = "罐车登记表-验证(app)", notes = "罐车登记表-验证(app)")
    @PostMapping(value = "/surveyor")
    public Result<?> surveyor(@RequestBody TankerRegistration tankerRegistration) {
        TankerRegistration byId = tankerRegistrationService.getById(tankerRegistration.getId());
        if (byId == null) {
            return Result.error("未找到对应数据");
        }
        tankerRegistrationService.updateById(tankerRegistration);
        new Thread(() -> {
            try {
                sendMessage(tankerRegistration);
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }).start();
        return Result.OK("操作成功！", null);
    }


    public void sendMessage(TankerRegistration tankerRegistration) {
        Integer status = tankerRegistration.getAuditStatus();
        if (2 == status || 3 == status) {
            String reason = 2 == status ? "三载审核已通过" : "驳回:" + tankerRegistration.getRejectReason();
            if (reason.length() > 20) reason = reason.substring(0, 17) + "...";// 确保 reason 不超过20个字符
            templateMessageUtil.register(tankerRegistration.getOpenId(), tankerRegistration.getPlateNo(),
                    tankerRegistration.getTransportStatus(), reason, tankerRegistration.getCreateTime());
        } else if (6 == status || 8 == status || 10 == status) {
            String message = 10 == status ? "请重新上传" : "待上传铅封";
            templateMessageUtil.sendLeadSealing(tankerRegistration.getOpenId(), tankerRegistration.getPlateNo(),
                    tankerRegistration.getDriverName(), message);
        }
    }

    /**
     * 编辑
     *
     * @param tankerRegistration
     * @return
     */
    @AutoLog(value = "罐车登记表-编辑")
    @ApiOperation(value = "罐车登记表-编辑", notes = "罐车登记表-编辑")
    @PutMapping(value = "/edit")
    public Result<?> edit(@RequestBody TankerRegistration tankerRegistration) {
        tankerRegistrationService.updateById(tankerRegistration);
        return Result.OK("编辑成功!");
    }

    /**
     * 通过id删除
     *
     * @param id
     * @return
     */
    @AutoLog(value = "罐车登记表-通过id删除")
    @ApiOperation(value = "罐车登记表-通过id删除", notes = "罐车登记表-通过id删除")
    @DeleteMapping(value = "/delete")
    public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
        tankerRegistrationService.removeById(id);
        return Result.OK("删除成功!");
    }

    /**
     * 批量删除
     *
     * @param ids
     * @return
     */
    @AutoLog(value = "罐车登记表-批量删除")
    @ApiOperation(value = "罐车登记表-批量删除", notes = "罐车登记表-批量删除")
    @DeleteMapping(value = "/deleteBatch")
    public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
        this.tankerRegistrationService.removeByIds(Arrays.asList(ids.split(",")));
        return Result.OK("批量删除成功!");
    }

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    @AutoLog(value = "罐车登记表-通过id查询")
    @ApiOperation(value = "罐车登记表-通过id查询", notes = "罐车登记表-通过id查询")
    @GetMapping(value = "/queryById")
    public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
        TankerRegistration tankerRegistration = tankerRegistrationService.getById(id);
        if (tankerRegistration == null) {
            return Result.error("未找到对应数据");
        }
        return Result.OK(tankerRegistration);
    }

    @AutoLog(value = "罐车登记表-通过openId查询最新记录")
    @ApiOperation(value = "罐车登记表-通过openId查询最新记录", notes = "罐车登记表-通过openId查询最新记录")
    @GetMapping(value = "/m/getByOpenId")
    public Result<?> getByOpenId(@RequestParam(name = "openId") String openId) {
        TankerRegistration tankerRegistration = tankerRegistrationService.getByOpenId(openId);
        if (tankerRegistration == null) {
            return Result.error("未找到对应数据");
        }
        return Result.OK(tankerRegistration);
    }

    @AutoLog(value = "罐车登记表-获取主界面未读消息")
    @ApiOperation(value = "罐车登记表-获取主界面未读消息", notes = "罐车登记表-获取主界面未读消息")
    @GetMapping(value = "/m/getUnread")
    public Result<?> getUnread() {
        LambdaQueryWrapper<TankerRegistration> wrapper = Wrappers.lambdaQuery();
        wrapper.in(TankerRegistration::getAuditStatus, 1, 2, 4, 6, 8, 11, 12);
        List<TankerRegistration> list = tankerRegistrationService.list(wrapper);
        // 定义所有可能的键及其初始值
        Map<String, Integer> initialMap = new HashMap<>();
        initialMap.put("三载审核", 0);
        initialMap.put("入场审核", 0);
        initialMap.put("车辆检验", 0);
        initialMap.put("入场磅单", 0);
        initialMap.put("品质检查", 0);
        initialMap.put("储运上传", 0);
        initialMap.put("磅单上传", 0);
        initialMap.put("车辆信息", 0);
        Map<String, Integer> map = new HashMap<>(initialMap);
        for (TankerRegistration tankerRegistration : list) {
            Integer status = tankerRegistration.getAuditStatus();
            Integer transportStatus = tankerRegistration.getTransportStatus();
            String key = getStatusKey(status, transportStatus);
            if (key != null) {
                map.compute(key, (k, v) -> (v == null) ? 0 : v + 1);
            }
        }
        return Result.OK(map);
    }

    private static String getStatusKey(int status, int transportStatus) {
        switch (status) {
            case 1:
                return "三载审核";
            case 2:
                return "入场审核";
            case 4:
                return (transportStatus == 1 || transportStatus == 3) ? "车辆检验" : "入场磅单";
            case 6:
                return (transportStatus == 2) ? "品质检查" : null;
            case 8:
            case 11:
                return "储运上传";
            case 12:
                return "磅单上传";
            default:
                return null;
        }
    }

    /**
     * 导出excel
     *
     * @param request
     * @param tankerRegistration
     */
    @RequestMapping(value = "/exportXls")
    public ModelAndView exportXls(HttpServletRequest request, TankerRegistration tankerRegistration) {
        return super.exportXls(request, tankerRegistration, TankerRegistration.class, "罐车登记表");
    }

    /**
     * 导出洗罐台账(提货)
     *
     * @param request
     * @param tankerRegistration
     */
    @RequestMapping(value = "/getCleanLedger")
    public ModelAndView getCleanLedger(HttpServletRequest request, TankerRegistration tankerRegistration) {
        QueryWrapper<TankerRegistration> queryWrapper = QueryGenerator.initQueryWrapper(tankerRegistration, request.getParameterMap());
        queryWrapper.isNotNull("last_time_cleaning_date");
        queryWrapper.isNotNull("last_time_cleaning_photos");
        List<TankerRegistration> pageList = tankerRegistrationService.list(queryWrapper);
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<TankerRegistration> exportList = null;
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            exportList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        } else {
            exportList = pageList;
        }
        List<CleanVO> collect = exportList.stream().map(tanker -> {
            CleanVO vo = new CleanVO();
            BeanUtils.copyProperties(tanker, vo);
            return vo;
        }).collect(Collectors.toList());
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, "发货台账");
        mv.addObject(NormalExcelConstants.CLASS, CleanVO.class);
        ExportParams exportParams = new ExportParams("发货台账", "导出人:" + sysUser.getRealname(), "发货台账");
        exportParams.setImageBasePath(upLoadPath);
        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
        mv.addObject(NormalExcelConstants.DATA_LIST, collect);
        return mv;
    }

    /**
     * 导出驳回台账(提货)
     *
     * @param request
     * @param tankerRegistration
     */
    @RequestMapping(value = "/getRejectLedger")
    public ModelAndView getRejectLedger(HttpServletRequest request, TankerRegistration tankerRegistration) {
        QueryWrapper<TankerRegistration> queryWrapper = QueryGenerator.initQueryWrapper(tankerRegistration, request.getParameterMap());
        queryWrapper.in("audit_status", 3, 5, 7, 9, 18);
        List<TankerRegistration> pageList = tankerRegistrationService.list(queryWrapper);
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<TankerRegistration> exportList = null;
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            exportList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        } else {
            exportList = pageList;
        }
        List<RejectVO> collect = exportList.stream().map(tanker -> {
            RejectVO vo = new RejectVO();
            BeanUtils.copyProperties(tanker, vo);
            return vo;
        }).collect(Collectors.toList());
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, "发货台账");
        mv.addObject(NormalExcelConstants.CLASS, RejectVO.class);
        ExportParams exportParams = new ExportParams("发货台账", "导出人:" + sysUser.getRealname(), "发货台账");
        exportParams.setImageBasePath(upLoadPath);
        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
        mv.addObject(NormalExcelConstants.DATA_LIST, collect);
        return mv;
    }

    /**
     * 导出发货台账(提货)
     *
     * @param request
     * @param tankerRegistration
     */
    @RequestMapping(value = "/getTakeDeliveryLedger")
    public ModelAndView getTakeDeliveryLedger(HttpServletRequest request, TankerRegistration tankerRegistration) {
        QueryWrapper<TankerRegistration> queryWrapper = QueryGenerator.initQueryWrapper(tankerRegistration, request.getParameterMap());
        queryWrapper.eq("transport_status", 1);
        queryWrapper.eq("audit_status", 13);
        List<TankerRegistration> pageList = tankerRegistrationService.list(queryWrapper);
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<TankerRegistration> exportList = null;
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            exportList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        } else {
            exportList = pageList;
        }
        List<TakeDeliveryLedgerVO> collect = exportList.stream().map(tanker -> {
            TakeDeliveryLedgerVO vo = new TakeDeliveryLedgerVO();
            BeanUtils.copyProperties(tanker, vo);
            return vo;
        }).collect(Collectors.toList());
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, "发货台账");
        mv.addObject(NormalExcelConstants.CLASS, TakeDeliveryLedgerVO.class);
        ExportParams exportParams = new ExportParams("发货台账", "导出人:" + sysUser.getRealname(), "发货台账");
        exportParams.setImageBasePath(upLoadPath);
        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
        mv.addObject(NormalExcelConstants.DATA_LIST, collect);
        return mv;
    }

    /**
     * 导出收货台账(送货)
     *
     * @param request
     * @param tankerRegistration
     */
    @RequestMapping(value = "/getDeliveryLedger")
    public ModelAndView getDeliveryLedger(HttpServletRequest request, TankerRegistration tankerRegistration) {
        QueryWrapper<TankerRegistration> queryWrapper = QueryGenerator.initQueryWrapper(tankerRegistration, request.getParameterMap());
        queryWrapper.eq("transport_status", 2);
        queryWrapper.eq("audit_status", 13);
        List<TankerRegistration> pageList = tankerRegistrationService.list(queryWrapper);
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<TankerRegistration> exportList = null;
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            exportList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        } else {
            exportList = pageList;
        }
        List<DeliveryLedgerVO> collect = exportList.stream().map(tanker -> {
            DeliveryLedgerVO vo = new DeliveryLedgerVO();
            BeanUtils.copyProperties(tanker, vo);
            return vo;
        }).collect(Collectors.toList());
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, "收货台账");
        mv.addObject(NormalExcelConstants.CLASS, DeliveryLedgerVO.class);
        ExportParams exportParams = new ExportParams("收货台账", "导出人:" + sysUser.getRealname(), "收货台账");
        exportParams.setImageBasePath(upLoadPath);
        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
        mv.addObject(NormalExcelConstants.DATA_LIST, collect);
        return mv;
    }

    /**
     * 导出液袋台账
     *
     * @param request
     * @param tankerRegistration
     */
    @RequestMapping(value = "/getLiquidBagLedger")
    public ModelAndView getLiquidBagLedger(HttpServletRequest request, TankerRegistration tankerRegistration) {
        QueryWrapper<TankerRegistration> queryWrapper = QueryGenerator.initQueryWrapper(tankerRegistration, request.getParameterMap());
        queryWrapper.eq("transport_status", 3);
        queryWrapper.eq("audit_status", 13);
        List<TankerRegistration> pageList = tankerRegistrationService.list(queryWrapper);
        LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        List<TankerRegistration> exportList = null;
        String selections = request.getParameter("selections");
        if (oConvertUtils.isNotEmpty(selections)) {
            List<String> selectionList = Arrays.asList(selections.split(","));
            exportList = pageList.stream().filter(item -> selectionList.contains(getId(item))).collect(Collectors.toList());
        } else {
            exportList = pageList;
        }
        List<LiquidBagLedgerVO> collect = exportList.stream().map(tanker -> {
            LiquidBagLedgerVO vo = new LiquidBagLedgerVO();
            BeanUtils.copyProperties(tanker, vo);
            return vo;
        }).collect(Collectors.toList());
        ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
        mv.addObject(NormalExcelConstants.FILE_NAME, "液袋台账");
        mv.addObject(NormalExcelConstants.CLASS, LiquidBagLedgerVO.class);
        ExportParams exportParams = new ExportParams("液袋台账", "导出人:" + sysUser.getRealname(), "液袋台账");
        exportParams.setImageBasePath(upLoadPath);
        mv.addObject(NormalExcelConstants.PARAMS, exportParams);
        mv.addObject(NormalExcelConstants.DATA_LIST, collect);
        return mv;
    }

    /**
     * 获取对象ID
     *
     * @return
     */
    private String getId(TankerRegistration item) {
        try {
            return PropertyUtils.getProperty(item, "id").toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 通过excel导入数据
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
        return super.importExcel(request, response, TankerRegistration.class);
    }

    @AutoLog(value = "罐车登记表-保存gps截图")
    @ApiOperation(value = "罐车登记表-保存gps截图", notes = "罐车登记表-保存gps截图")
    @PostMapping("/gpsKeep")
    public Result<?> gpsKeep(@RequestBody TankerRegistration tankerRegistration) {
        String gpsKeep = tankerRegistration.getGpsKeep();
        final String[] base64Array = gpsKeep.split(",");
        // base64转为流
        String dataUir = base64Array[0];
        String data = base64Array[1];
        MultipartFile multipartFile = new Base64ToMultipartFile(data, dataUir);
        String bizPath = DateUtil.formatDate(new Date());
        String savePath = CommonUtils.upload(multipartFile, bizPath, uploadType);
        tankerRegistration.setGpsKeep(savePath.length() > 54 ? savePath.substring(54) : savePath);
        tankerRegistrationService.updateById(tankerRegistration);
        return Result.OK("保存成功", null);
    }

    /**
     * 文件上传统一方法
     *
     * @param request 请求
     * @return Result
     */
    @AutoLog(value = "罐车登记表-上传照片")
    @ApiOperation(value = "罐车登记表-上传照片", notes = "罐车登记表-上传照片")
    @PostMapping(value = "/m/upload")
    public Result<?> upload(HttpServletRequest request) throws IOException {
        Result<Object> result = new Result<>();
        String savePath = "";
        String bizPath = DateUtil.formatDate(new Date());
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        MultipartFile file = multipartRequest.getFile("file");// 获取上传文件对象
        String name = request.getParameter("name");
        String text = request.getParameter("text");
        if (file != null) {
            if ("66".equals(name)) {
                file = addWatermarkToImage(file, "中储粮油脂（天津）有限公司");
            } else if (StringUtils.isNotBlank(text)) {
                file = addWatermarkToImage(file, text);
            }
            if ("99".equals(name)) {
                MultipartFile multipartFile = saveFileWithoutWaterMark(file);
                savePath = CommonUtils.upload(multipartFile, bizPath, uploadType);
            } else {
                MultipartFile multipartFile = saveFileWithoutWaterMark1(file);
                savePath = CommonUtils.upload(multipartFile, bizPath, uploadType);
            }
        }
        if (oConvertUtils.isNotEmpty(savePath)) {
            result.setMessage(savePath.length() > 54 ? savePath.substring(54) : savePath);
            result.setSuccess(true);
            if ("99".equals(name)) {
                RecognizeGeneralTableResponse ocr = HuaweiCloudUtil.recognizeGeneralTable(savePath);
                List<IdentifyRecord> identifyRecords = handleDocumentationTableDataOfOcr(ocr);
                try {
                    identifyRecordService.saveBatch(identifyRecords);
                } catch (Exception e) {
                    log.error("识别记录保存失败", e);
                }
                identifyRecords.forEach(item -> {
                    item.setShipmentTime(convertToStandardFormat(item.getShipmentTime()));
                    item.setUnloadingTime(convertToStandardFormat(item.getUnloadingTime()));
                });
                result.setResult(identifyRecords);
            } else if ("98".equals(name)){
                RecognizeIdCardResponse recognizeIdCardResponse = HuaweiCloudUtil.recognizeIdCard(savePath);
                if (recognizeIdCardResponse != null){
                    result.setResult(recognizeIdCardResponse.getResult());
                } else {
                    result.setSuccess(false);
                    result.setMessage("身份证正面识别失败！请检查图片是否清晰！");
                }
            } else if ("97".equals(name) || "96".equals(name)){
                RecognizeVehicleLicenseResponse recognizeVehicleLicenseResponse = HuaweiCloudUtil.recognizeVehicleLicense(savePath);
                if (recognizeVehicleLicenseResponse != null){
                    result.setResult(recognizeVehicleLicenseResponse.getResult());
                } else {
                    result.setSuccess(false);
                    result.setMessage("行驶证主页识别失败！请检查图片是否清晰！");
                }
            }
        } else {
            result.setMessage("上传失败！");
            result.setSuccess(false);
        }
        return result;
    }

    public List<IdentifyRecord> handleDocumentationTableDataOfOcr(RecognizeGeneralTableResponse response) {
        if (response == null) {
            return null;
        }
        List<IdentifyRecord> resultList = new ArrayList<>();
        UUID uuid = UUID.randomUUID();
        try {
            GeneralTableResult result = response.getResult();
            if (result == null || result.getWordsRegionList() == null) {
                return resultList;
            }
            List<WordsRegionList> wordsRegionList = result.getWordsRegionList();
            List<String> currentRow = new ArrayList<>(); // 临时存储当前行数据
            boolean isHeaderSkipped = false; // 标记是否已跳过表头
            for (WordsRegionList wordsRegion : wordsRegionList) {
                if ("table".equalsIgnoreCase(wordsRegion.getType())) {
                    List<GeneralTableWordsBlockList> wordsBlockList = wordsRegion.getWordsBlockList();
                    for (int i = 0; i < wordsBlockList.size(); i++) {
                        GeneralTableWordsBlockList wordsBlock = wordsBlockList.get(i);
                        // 处理文本内容，去除换行符并去除首尾空白
                        String textContent = wordsBlock.getWords() != null ?
                                wordsBlock.getWords().replaceAll("[\\r\\n]+", "").trim() : "";
                        // 只有非空字符串才添加到当前行
                        if (!textContent.isEmpty()) {
                            currentRow.add(textContent);
                        }
                        // 每7列处理一次（表格列数）
                        if ((i + 1) % 7 == 0) { // i从0开始，+1后取模
                            if (isHeaderSkipped) {
                                // 从第二行开始处理（跳过表头）
                                IdentifyRecord identifyRecord = new IdentifyRecord()
                                        .setSerialNumber(getSafeString(currentRow, 0))
                                        .setShipmentTime(getSafeString(currentRow, 1))
                                        .setProductType(getSafeString(currentRow, 2))
                                        .setGeneticallyModified(getSafeString(currentRow, 3))
                                        .setPickupLocation(getSafeString(currentRow, 4))
                                        .setDeliveryLocation(getSafeString(currentRow, 5))
                                        .setUnloadingTime(getSafeString(currentRow, 6))
                                        .setUuid(uuid.toString());
                                resultList.add(identifyRecord);
                            } else {
                                // 标记已处理表头
                                isHeaderSkipped = true;
                            }
                            currentRow.clear(); // 清空当前行
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("ocr数据处理异常", e);
        }
        return resultList;
    }

    /**
     * 安全获取列表中的元素
     */
    private String getSafeString(List<String> list, int index) {
        if (list != null && index >= 0 && index < list.size()) {
            return list.get(index);
        }
        return "";
    }

    public MultipartFile saveFileWithoutWaterMark1(MultipartFile file) {
        try {
            if ((1024 * 1024 * 0.1) <= file.getSize()) {
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                //小于 1M 的
                if ((1024 * 1024 * 0.1) <= file.getSize() && file.getSize() <= (1024 * 1024)) {
                    Thumbnails.of(file.getInputStream()).scale(0.8f).outputQuality(0.5f).toOutputStream(outputStream);
                }
                // 1 - 2M 的
                else if ((1024 * 1024) < file.getSize() && file.getSize() <= (1024 * 1024 * 2)) {
                    Thumbnails.of(file.getInputStream()).scale(0.7f).outputQuality(0.4f).toOutputStream(outputStream);
                }// 2M 以上的
                else if ((1024 * 1024 * 2) < file.getSize()) {
                    Thumbnails.of(file.getInputStream()).scale(0.5f).outputQuality(0.2f).toOutputStream(outputStream);
                }

                byte[] bytes = outputStream.toByteArray();
                InputStream inputStream = new ByteArrayInputStream(bytes);
                // 转换城新的 MultipartFile 对象
                MultipartFile fileInfos = new MockMultipartFile(Objects.requireNonNull(file.getOriginalFilename()), file.getOriginalFilename(), file.getContentType(), inputStream);
                log.info("源文件大小:{}压缩后的大小:{}", file.getSize(), fileInfos.getSize());
                return fileInfos;
            }
            return file;

        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }

    public MultipartFile saveFileWithoutWaterMark(MultipartFile file) {
        try {
            // 检查是否为空文件
            if (file == null || file.isEmpty()) {
                return file;
            }

            // 获取原始文件信息
            String originalFilename = Objects.requireNonNull(file.getOriginalFilename());
            String contentType = file.getContentType();
            long originalSize = file.getSize();

            // 检查是否为图片
            if (!contentType.startsWith("image/")) {
                return file;
            }

            // 读取原始图片的像素尺寸
            BufferedImage originalImage = ImageIO.read(file.getInputStream());
            int originalWidth = originalImage.getWidth();
            int originalHeight = originalImage.getHeight();

            // 检查像素尺寸是否符合要求
            boolean isWidthValid = originalWidth >= 15 && originalWidth <= 8192;
            boolean isHeightValid = originalHeight >= 15 && originalHeight <= 8192;
            boolean isPixelValid = isWidthValid && isHeightValid;

            // 计算 Base64 编码后的大小（估算：原始字节数 * 4/3）
            double base64EstimatedSize = originalSize * 4.0 / 3.0;

            // 检查是否需要压缩
            if (isPixelValid && base64EstimatedSize <= 10 * 1024 * 1024) {
                // 原始文件完全符合要求
                return file;
            }

            // 需要压缩：调整尺寸和质量
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            double scale = 1.0;

            // 动态计算缩放比例（优先满足像素限制）
            if (originalWidth > 8192 || originalHeight > 8192) {
                scale = Math.min(8192.0 / originalWidth, 8192.0 / originalHeight);
            } else if (originalWidth < 15 || originalHeight < 15) {
                scale = Math.max(15.0 / originalWidth, 15.0 / originalHeight);
            }

            // 动态计算输出质量（优先满足 Base64 大小限制）
            double quality = 1.0;
            if (base64EstimatedSize > 10 * 1024 * 1024) {
                quality = (10 * 1024 * 1024 * 3.0 / 4.0) / originalSize;
                quality = Math.min(quality, 1.0); // 保证质量不超过 100%
            }

            // 执行压缩
            Thumbnails.of(file.getInputStream())
                    .scale(scale)
                    .outputQuality(quality)
                    .toOutputStream(outputStream);

            byte[] compressedBytes = outputStream.toByteArray();
            InputStream compressedInputStream = new ByteArrayInputStream(compressedBytes);

            // 生成新的 MultipartFile 对象
            MultipartFile compressedFile = new MockMultipartFile(
                    originalFilename,
                    originalFilename,
                    contentType,
                    compressedInputStream
            );

            // 日志记录
            log.info("源文件大小: {} B, 压缩后大小: {} B", originalSize, compressedFile.getSize());
            log.info("源文件尺寸: {}x{}, 压缩后尺寸: {}x{}",
                    originalWidth, originalHeight,
                    (int) (originalWidth * scale), (int) (originalHeight * scale));

            return compressedFile;
        } catch (Exception e) {
            log.error("图片压缩失败: {}", e.getMessage(), e);
            return null;
        }
    }

    @AutoLog(value = "注册表-获取openid")
    @ApiOperation(value = "注册表-获取openid", notes = "注册表-获取openid")
    @GetMapping(value = "/m/getOpenid")
    public Result<?> getOpenid(@RequestParam(name = "code", required = true) String code) {
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + APPID + "&secret=" + SECRET + "&code=" + code + "&grant_type=authorization_code";
        String s = doGet(url);
        JSONObject jsonObject = JSONObject.parseObject(s);
        if (jsonObject.containsKey("openid")) {
            return Result.OK(jsonObject.getString("openid"));
        }
        return Result.error("失败");
    }

    private String doGet(String url) {
        try {
            URL apiUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
            connection.setRequestMethod("GET");
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuilder response = new StringBuilder();
                while ((inputLine = reader.readLine()) != null) {
                    response.append(inputLine);
                }
                reader.close();
                return response.toString();
            } else {
                return "";
            }
        } catch (IOException e) {
            log.error("doGet方法出现异常", e);
        }
        return "";
    }

    @GetMapping("/m/getImgFormObs")
    public ResponseEntity<byte[]> getImgFormObs(@RequestParam(name = "url") String url) throws IOException {
        String obsObjectName = getObsObjectName(url);
        InputStream input = ObsBootUtil.getObsFile(obsObjectName);
        // 使用 ByteArrayOutputStream 读取所有字节
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = input.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        // 获取字节数组
        byte[] imageBytes = baos.toByteArray();
        // 关闭资源
        baos.close();
        input.close();
        // 设置响应头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(getMediaTypeFromFileName(obsObjectName)); // 根据实际图片类型设置
        return new ResponseEntity<>(imageBytes, headers, HttpStatus.OK);
    }

    private MediaType getMediaTypeFromFileName(String fileName) {
        // 创建一个映射表，用于根据文件扩展名设置 Content-Type
        Map<String, MediaType> mediaTypes = new HashMap<>();
        mediaTypes.put("jpg", MediaType.IMAGE_JPEG);
        mediaTypes.put("jpeg", MediaType.IMAGE_JPEG);
        mediaTypes.put("png", MediaType.IMAGE_PNG);
        mediaTypes.put("gif", MediaType.IMAGE_GIF);

        // 获取文件扩展名
        String extension = getFileExtension(fileName);

        // 根据扩展名设置 Content-Type
        return mediaTypes.getOrDefault(extension, MediaType.APPLICATION_OCTET_STREAM);
    }

    private String getFileExtension(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return "";
        }
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1) {
            return "";
        }
        return fileName.substring(dotIndex + 1).toLowerCase();
    }

    private String getObsObjectName(String url) {
        // 找到最后一个 '/'
        int lastSlashIndex = url.lastIndexOf('/');
        // 如果找不到最后一个 '/'，则返回原字符串
        if (lastSlashIndex == -1) {
            log.error("非法的路径: {}", url);
            throw new RuntimeException("非法的路径");
        }

        // 找到倒数第二个 '/'
        int secondLastSlashIndex = url.lastIndexOf('/', lastSlashIndex - 1);
        // 如果找不到倒数第二个 '/'，则返回最后一个 '/' 之后的内容
        if (secondLastSlashIndex == -1) {
            log.error("非法的路径: {}", url);
            throw new RuntimeException("非法的路径");
        }

        // 截取倒数第二个 '/' 之后的内容
        return url.substring(secondLastSlashIndex + 1);
    }


    @AutoLog(value = "罐车登记表-统计图卡片数据")
    @ApiOperation(value = "罐车登记表-统计图卡片数据", notes = "罐车登记表-统计图卡片数据")
    @PostMapping(value = "/reportCard")
    public Result<?> reportCard(@RequestBody ReportDTO reportDTO) {
        String startTime = reportDTO.getStartTime();
        String endTime = reportDTO.getEndTime();
        QueryWrapper<TankerRegistration> wrapper = Wrappers.query();
        wrapper.select("COUNT(*) AS total, COUNT(DISTINCT plate_no) AS totalUnique");
        wrapper.in("audit_status", 2, 4, 6, 8, 10, 11, 12, 13);
        wrapper.ge(StringUtils.isNotBlank(startTime), "create_time", startTime);
        wrapper.le(StringUtils.isNotBlank(endTime), "create_time", endTime);
        List<Map<String, Object>> maps = tankerRegistrationService.listMaps(wrapper);
        wrapper.clear();
        wrapper.select("COUNT(*) AS noPassTotal, COUNT(DISTINCT plate_no) AS noPassUnique");
        wrapper.in("audit_status", 3, 5);
        wrapper.ge(StringUtils.isNotBlank(startTime), "create_time", startTime);
        wrapper.le(StringUtils.isNotBlank(endTime), "create_time", endTime);
        List<Map<String, Object>> noPass = tankerRegistrationService.listMaps(wrapper);
        maps.addAll(noPass);
        wrapper.clear();
        wrapper.select("COUNT(*) AS unqualifiedTotal, COUNT(DISTINCT plate_no) AS unqualifiedUnique");
        wrapper.in("audit_status", 7, 9);
        wrapper.ge(StringUtils.isNotBlank(startTime), "create_time", startTime);
        wrapper.le(StringUtils.isNotBlank(endTime), "create_time", endTime);
        List<Map<String, Object>> unqualified = tankerRegistrationService.listMaps(wrapper);
        maps.addAll(unqualified);
        return Result.OK(maps);
    }

    @AutoLog(value = "罐车登记表-柱状图")
    @ApiOperation(value = "罐车登记表-柱状图", notes = "罐车登记表-柱状图")
    @PostMapping(value = "/reportBarQualified")
    public Result<?> reportBarQualified(@RequestBody ReportDTO reportDTO) {
        String startTime = reportDTO.getStartTime();
        String endTime = reportDTO.getEndTime();
        QueryWrapper<TankerRegistration> wrapper = Wrappers.query();
        wrapper.select("CONCAT(YEAR(create_time), '-', LPAD(MONTH(create_time), 2, '0')) AS month",
                "SUM(CASE WHEN audit_status IN (2, 4, 6, 8, 10, 11, 12, 13) THEN 1 ELSE 0 END) AS qualifiedCount",
                "COUNT(DISTINCT CASE WHEN audit_status IN (2, 4, 6, 8, 10, 11, 12, 13) THEN plate_no END) AS qualifiedDistinct");
        wrapper.ge(StringUtils.isNotBlank(startTime), "create_time", startTime);
        wrapper.le(StringUtils.isNotBlank(endTime), "create_time", endTime);
        wrapper.groupBy("month");
        List<Map<String, Object>> maps = tankerRegistrationService.listMaps(wrapper);
        return Result.OK(maps);
    }

    @AutoLog(value = "罐车登记表-柱状图")
    @ApiOperation(value = "罐车登记表-柱状图", notes = "罐车登记表-柱状图")
    @PostMapping(value = "/reportBarNoPass")
    public Result<?> reportBarNoPass(@RequestBody ReportDTO reportDTO) {
        String startTime = reportDTO.getStartTime();
        String endTime = reportDTO.getEndTime();
        QueryWrapper<TankerRegistration> wrapper = Wrappers.query();
        wrapper.select("CONCAT(YEAR(create_time), '-', LPAD(MONTH(create_time), 2, '0')) AS month",
                "SUM(CASE WHEN audit_status IN (3, 5) THEN 1 ELSE 0 END) AS noPassCount",
                "COUNT(DISTINCT CASE WHEN audit_status IN (3, 5) THEN plate_no END) AS noPassDistinct");
        wrapper.ge(StringUtils.isNotBlank(startTime), "create_time", startTime);
        wrapper.le(StringUtils.isNotBlank(endTime), "create_time", endTime);
        wrapper.groupBy("month");
        List<Map<String, Object>> maps = tankerRegistrationService.listMaps(wrapper);
        return Result.OK(maps);
    }

    @AutoLog(value = "罐车登记表-柱状图")
    @ApiOperation(value = "罐车登记表-柱状图", notes = "罐车登记表-柱状图")
    @PostMapping(value = "/reportBarUnQualified")
    public Result<?> reportBarUnQualified(@RequestBody ReportDTO reportDTO) {
        String startTime = reportDTO.getStartTime();
        String endTime = reportDTO.getEndTime();
        QueryWrapper<TankerRegistration> wrapper = Wrappers.query();
        wrapper.select("CONCAT(YEAR(create_time), '-', LPAD(MONTH(create_time), 2, '0')) AS month",
                "SUM(CASE WHEN audit_status IN (7, 9) THEN 1 ELSE 0 END) AS unQualifiedCount",
                "COUNT(DISTINCT CASE WHEN audit_status IN (7, 9) THEN plate_no END) AS unQualifiedDistinct");
        wrapper.ge(StringUtils.isNotBlank(startTime), "create_time", startTime);
        wrapper.le(StringUtils.isNotBlank(endTime), "create_time", endTime);
        wrapper.groupBy("month");
        List<Map<String, Object>> maps = tankerRegistrationService.listMaps(wrapper);
        return Result.OK(maps);
    }

    public MultipartFile addWatermarkToImage(MultipartFile file, String watermarkText) throws IOException {
        // 读取原始图片
        try (InputStream inputStream = file.getInputStream()) {
            BufferedImage originalImage = ImageIO.read(inputStream);
            if (originalImage == null) {
                throw new IllegalArgumentException("读取图片失败");
            }
            int imgWidth = originalImage.getWidth();
            int imgHeight = originalImage.getHeight();
            // 动态计算字体大小
            int fontSize = Math.min(imgWidth / 30, imgHeight / 13); // 可以根据需要调整这个比例
            if (fontSize < 10) { // 防止字体过小
                fontSize = 10;
            }
            // 设置水印字体和颜色
            Font font = new Font("Serif", Font.BOLD, fontSize);
            // 创建Graphics2D对象用于绘制水印
            Graphics2D g2d = originalImage.createGraphics();
            g2d.setFont(font);
            g2d.setColor(Color.RED);
            String s = DateUtils.formatDateTime();
            // 计算水印位置（这里选择右下角）
            FontMetrics fm = g2d.getFontMetrics();
            int textWidth = fm.stringWidth(watermarkText);
            int textHeight = fm.getHeight();
            int dateTextWidth = fm.stringWidth(s);

            // 设置水印在左上角，并预留一些边距
            int margin = 10;
            int x = margin; // 从左边开始，距离左侧有一个边距
            int y = textHeight + margin; // 将文本放在顶部，距离顶部有一个边距

            // 绘制水印文字
            g2d.drawString(s, x, y); // 第一行
            g2d.drawString(watermarkText, x, y + textHeight); // 第二行

            // 清理资源
            g2d.dispose();

            // 确定图片格式
            String formatName = getFormatName(file.getContentType());

            // 将带有水印的图片写入输出流
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                ImageIO.write(originalImage, formatName, outputStream);

                // 创建新的MultipartFile对象
                return new ByteArrayMultipartFile(
                        file.getName(),
                        file.getOriginalFilename(),
                        file.getContentType(),
                        outputStream.toByteArray()
                );
            }
        }
    }

    private String getFormatName(String contentType) {
        if (contentType != null && contentType.contains("/")) {
            return contentType.substring(contentType.indexOf("/") + 1);
        }
        return "png"; // 默认格式
    }

    // 辅助类，用于创建新的MultipartFile对象
    public static class ByteArrayMultipartFile implements MultipartFile {
        private final String name;
        private final String originalFilename;
        private final String contentType;
        private final byte[] content;

        public ByteArrayMultipartFile(String name, String originalFilename, String contentType, byte[] content) {
            this.name = name;
            this.originalFilename = originalFilename;
            this.contentType = contentType;
            this.content = content;
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getOriginalFilename() {
            return originalFilename;
        }

        @Override
        public String getContentType() {
            return contentType;
        }

        @Override
        public boolean isEmpty() {
            return content.length == 0;
        }

        @Override
        public long getSize() {
            return content.length;
        }

        @Override
        public byte[] getBytes() throws IOException {
            return content.clone();
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return new ByteArrayInputStream(content);
        }

        @Override
        public void transferTo(File dest) throws IOException, IllegalStateException {
            try (FileOutputStream fos = new FileOutputStream(dest)) {
                fos.write(content);
            }
        }
    }

    @AutoLog(value = "罐车登记表-手动备份")
    @ApiOperation(value = "罐车登记表-手动备份", notes = "罐车登记表-手动备份")
    @PostMapping(value = "/test")
    public Result<?> test(@RequestParam(name = "createTime") String createTime,
                          @RequestParam(name = "endTime") String endTime) throws IOException {
        log.info("createTime: {}, endTime: {}", createTime, endTime);
        log.info("---------------开始执行备份----------------");
        String backupsDir = "D:\\zcl_backups\\poi";
        LambdaQueryWrapper<TankerRegistration> wrapper = Wrappers.lambdaQuery();
        wrapper.between(TankerRegistration::getCreateTime, createTime, endTime);
        // 将查询结果转换为不可变列表
        List<TankerRegistration> tankerRegistrations = tankerRegistrationService.list(wrapper);
        List<TankerRegistration> picList = new ArrayList<>(tankerRegistrations);
        log.info("获取的记录数：{}", tankerRegistrations.size());
        String start = createTime.replaceAll("-", "").replaceAll(" ", "").replaceAll(":", "");
        String end = endTime.replaceAll("-", "").replaceAll(" ", "").replaceAll(":", "");
        String filename = start + "-" + end + ".xlsx";
        String zipName = start + "-" + end + "zip.zip";
        try {
            Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(
                    start + "-" + end + "备份", "备份"), TankerRegistration.class, tankerRegistrations);
            File savefile = new File(backupsDir);
            if (!savefile.exists()) {
                boolean mkDirs = savefile.mkdirs();
                if (!mkDirs) {
                    throw new RuntimeException("创建文件夹失败");
                }
            }
            FileOutputStream fos = new FileOutputStream(backupsDir + "\\" + filename);
            workbook.write(fos);
            fos.close();
            log.info("---------------备份完成----------------");
            // 获取文件大小并转换为KB
            Path filePath = Paths.get(backupsDir, filename);
            long fileSizeBytes = Files.size(filePath);
            int fileSizeKB = (int) (fileSizeBytes / 1024);

            // 创建临时目录用于存储各个TankerRegistration的zip文件
            Path tempZipDir = Files.createTempDirectory("tankerRegistration_zips");

            try {
                // 获取所有标记为图片的字段
                List<Field> imageFields = MyScheduled.getImageFields(TankerRegistration.class);

                // 为每个TankerRegistration创建独立的zip文件
                for (TankerRegistration backup : picList) {
                    try {
                        MyScheduled.createZipForTankerRegistration(backup, imageFields, tempZipDir);
                    } catch (Exception e) {
                        log.error("为TankerRegistration {} 创建zip文件时出错", backup.getId(), e);
                    }
                }

                // 将所有独立的zip文件打包成一个总的zip文件
                Path finalZipPath = Paths.get(backupsDir, zipName);
                try (ZipOutputStream mainZos = new ZipOutputStream(Files.newOutputStream(finalZipPath))) {
                    File[] zipFiles = tempZipDir.toFile().listFiles((dir, name) -> name.endsWith(".zip"));
                    if (zipFiles != null) {
                        for (File zipFile : zipFiles) {
                            MyScheduled.addFileToZip(mainZos, zipFile, zipFile.getName());
                        }
                    }
                }

                // 获取总zip文件大小并转换为KB
                long zipSizeBytes = Files.size(finalZipPath);
                int zipSizeKB = (int) (zipSizeBytes / 1024);

                Backups backups = new Backups();
                backups.setFileName(filename).setFileUrl(filename).setFileSize(fileSizeKB)
                        .setZipName(zipName).setZipUrl(zipName).setZipSize(zipSizeKB)
                        .setBackupsTime(new Date()).setStatus(1);
                backupsService.save(backups);
            } finally {
                // 清理临时目录
                MyScheduled.deleteDirectory(tempZipDir.toFile());
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return Result.OK();
    }

    /**
     * 将日期字符串转换为标准格式
     *
     * @param input 日期字符串
     * @return 转换后的日期字符串
     */
    public static String convertToStandardFormat(String input) {
        input = input.replaceAll(" ", "").trim();
        List<DateTimeFormatter> formatters = Arrays.asList(
                DateTimeFormatter.ofPattern("yyyy年M月d日"),
                DateTimeFormatter.ofPattern("yyyy年M月dd日")
        );
        for (DateTimeFormatter formatter : formatters) {
            try {
                LocalDate date = LocalDate.parse(input, formatter);
                return date.format(DateTimeFormatter.ISO_LOCAL_DATE); // 输出格式：yyyy-MM-dd
            } catch (Exception e) {
                // 如果解析失败，继续下一个格式
            }
        }
        return null;
    }

}
